<?php
/**
 * FSM：file system mapping
 */
class FSM {

    public static function p($v) {
        print_r($v);
        echo PHP_EOL;
    }

    public static function pe($v) {
        print_r($v);
        exit(PHP_EOL);
    }

    //获取某个目录下的子目录或文件
    public static function getDirSub($dir) {
        $dirs = scandir($dir);
        $rs = [];
        foreach ($dirs as $v) {
            if (!in_array($v, ['.', '..'])) {
                $rs[] = $v;
            }
        }
        return $rs;
    }

    //获取相对路径
    public static function getRelatePath($path, $prefix='') {
        return str_replace($prefix, '', $path);
    }

    //改变路径分割符
    public static function changePath($path) {
        $path = str_replace('\\', '/', $path);
        return $path;
    }

    //组装路径, 返回的字符串最后没有‘/’
    public static function buildPath($arr) {
        $str = implode('/', $arr);
        $path = str_replace(['\\', '//'], ['/','/'], $str);
        return $path;
    }

    //替换文件名中的某些字符串
    public static function replace($search, $replace, $arr) {
        $list = [];
        foreach ($arr as $v) {
            $list[$v] = str_replace($search, $replace, $v); 
        }

        return $list;
    }

    //过滤出需要的文件
    public static function filterFile($arr, $key) {
        $list = [];
        foreach ($arr as $v) {
            if (strpos($v, $key) !== false) {
                $list[] = $v;
            }
        }
        return $list;
    }

    //生成文件名对应的哈希值
    public static function makeFileCode($arr) {
        $list = [];
        foreach ($arr as $v) {
            $list[$v] = md5($v);
        }
        return $list;
    }

    //将文件名改为标题， 用于页面显示
    public static function changeToTitle($str) {
        $str = str_replace(['.md', '/'], ['', '-'], $str);
        $str = preg_replace('/\[\d+\]/', '', $str);
        return $str;
    }

    /**
     * 读取所有文件
     * @param string
     * @return array
     */
    public static function getFiles($dir) {
        $dir = realpath($dir);
        $dir = self::buildPath([$dir]);
        
        $files = [];
        $subdirs = [];

        //第一层
        $fs = self::getDirSub($dir);
        foreach ($fs as $v) {
            $path = self::buildPath([$dir, $v]);
            if (is_dir($path)) {
                $subdirs[] = $path;
            } else {
                $files[] = $path;
            }
        }

        while(!empty($subdirs)) {
            $subdir = array_shift($subdirs);
            $fs = self::getDirSub($subdir);
            foreach($fs as $v) {
                $path = self::buildPath([$subdir, $v]);
                if (is_dir($path)) {
                    $subdirs[] = $path;
                } else {
                    $files[] = $path;
                }
            }
        }

        sort($files);
        return $files;
        
    }

    //获取文件系统的json格式
    public static function getTree($dir) 
    {
        $dir = realpath($dir);
        $dir = self::buildPath([$dir]);
        $subdirs = [];
        $nodes = []; //每层目录都转化为一个对象，放在这个里边
        $root = new FSMD('/');

        //第一层
        foreach (self::getDirSub($dir) as $v) {
            $path = self::buildPath([$dir, $v]);
            if (is_dir($path)) {
                $d = new FSMD($path, $dir);
                $subdirs[] = $d;
                $nodes[$path] = $d;
                $root->append($path);
            } else {
                $f = new FSMF($path, $dir);
                $nodes[$path] = $f;
                $root->append($path);
            }
        }

        //第二层以后
        while(!empty($subdirs)) {
            $subdir = array_shift($subdirs);
            foreach(self::getDirSub($subdir->path) as $v) {
                $path = self::buildPath([$subdir->path, $v]);
                if (is_dir($path)) {
                    $d = new FSMD($path, $dir);
                    $subdirs[] = $d;
                    $nodes[$path] = $d;
                } else {
                    $f = new FSMF($path, $dir);
                    $nodes[$path] = $f;
                }
                $nodes[$subdir->path]->append($path);
            }
        }

        $GLOBALS['nodes'] = $nodes;
        return $root->out();
    }
}

// file system mapping dir
class FSMD {
    public $path='';
    public $relpath = '';
    public $current='';
    public $acl = [];
    public $children = [];
    public function __construct($path, $prefix='', $acl=[])
    {
        $this->path = $path; //绝对路径
        
        $this->relpath = str_replace($prefix, '', $path); //相对路径
        $this->relpath = ltrim($this->relpath,'/');

        $arr = explode('/', $path);
        $this->current = end($arr);

        $this->acl = $acl;
    }

    public function append($path) {
        $this->children[] = $path;
    }
    public function out() {
        $childs = [];
        foreach ($this->children as $v) {
            $childs[] = $GLOBALS['nodes'][$v]->out();
        }
        $items = [];
        $items[] = '"type":"dir"';
        $items[] = '"code":"'.md5($this->relpath).'"';
        $items[] = '"relpath":"'.$this->relpath.'"';
        $items[] = '"current":"'.$this->current.'"';
        $items[] = '"title":"'.FSM::changeToTitle($this->current).'"';
        $items[] = '"acl":"'.implode(',', $this->acl).'"';
        $items[] = '"children":['.implode(',', $childs).']';

        return '{'.implode(',',$items).'}';
    }
}

// file system mapping file
class FSMF {
    public $path='';
    public $relpath='';
    public $current='';
    public $acl = [];

    public function __construct($path, $prefix='', $acl=[])
    {
        $this->path = $path;//绝对路径
        $this->relpath = str_replace($prefix, '', $path);//相对路径
        $this->relpath = ltrim($this->relpath,'/');
        
        $arr = explode('/', $path);
        $this->current = end($arr);

        $this->acl = $acl;
    }

    public function out() {
        $items = [];
        $items[] = '"type":"file"';
        $items[] = '"code":"'.md5($this->relpath).'"';
        $items[] = '"relpath":"'.$this->relpath.'"';
        $items[] = '"current":"'.$this->current.'"';
        $items[] = '"title":"'.FSM::changeToTitle($this->current).'"';
        $items[] = '"acl":"'.implode(',', $this->acl).'"';

        return '{'.implode(',',$items).'}';
    }
}

// $files = FSM::getFiles('../src');
// sort($files);   
// print_r($files);

// $tree = FSM::getTree('../src');
// print_r($tree);

// print_r(explode('/', '/'));

